package org.framework.define.mapper;

import javax.persistence.Transient;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.jdbc.core.RowMapper;

public abstract class AbstractObjectRowMapper<T> implements RowMapper<T> {
	
	private final Logger log = LogManager.getLogger(AbstractObjectRowMapper.class);
			
	private final Class<? extends T> mappedClass;

	private final HashMap<String, Object> rsMap = new HashMap<String, Object>();
	
	public AbstractObjectRowMapper(Class<? extends T> mappedClass) {
		this.mappedClass = mappedClass;
	}
	
	public T mapRow(ResultSet rs, int rowNum) throws SQLException {
		T entity = null;
		
		try {
			entity = mappedClass.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		
		ResultSetMetaData rsmd = rs.getMetaData();
		
		int columnCount = rsmd.getColumnCount();
		
		for (int index = 1; index <= columnCount; ++index) {
			rsMap.put(rsmd.getColumnLabel(index).toLowerCase().replace("_", ""), rs.getObject(index));
		}
		
		Field [] fields =  mappedClass.getDeclaredFields();
		AccessibleObject.setAccessible(fields, true);
		for (Field field : fields) {
			try {
				if(!field.isAnnotationPresent(Transient.class) && hasColumn(field.getName())) {
					field.set(entity, rsMap.get(field.getName().toLowerCase()));
				}
			} catch (IllegalArgumentException e) {
				log.error(e);
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				log.error(e);
				e.printStackTrace();
			}
		}
		
		mapRowHandler(rs, entity);
		return entity;
	}
	
	protected boolean hasColumn(String columnName){
		return rsMap.containsKey(columnName.toLowerCase());
	}
	
	protected abstract void mapRowHandler(ResultSet rs, T entity);
	
	public Class<? extends T> getMappedClass() {
		return mappedClass;
	}
}
